package com.allqj.sfx.ddd.member.domain.service;

import com.allqj.sfx.ddd.member.domain.entity.flow.*;
import com.allqj.sfx.ddd.member.domain.enums.FlowDefStatusEnum;
import com.allqj.sfx.ddd.member.domain.enums.FlowExceptionEnum;
import com.allqj.sfx.ddd.member.domain.enums.FlowStatusEnum;
import com.allqj.sfx.ddd.member.domain.repository.*;
import com.allqj.sfx.infrastructure.utils.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author sjw
 * @className FlowEngineService
 * @date 2022/7/21 17:56
 * @description: 工作流引擎
 */
@Component
public class FlowEngineService {

    private final FlowTypeRepository flowTypeRepository;
    private final FlowDefRepository flowDefRepository;
    private final FlowTaskDefRepository flowTaskDefRepository;
    private final FlowInstanceRepository flowInstanceRepository;
    private final FlowInstanceTaskRepository flowInstanceTaskRepository;
    private Logger LOGGER = LoggerFactory.getLogger(FlowEngineService.class);

    public FlowEngineService(FlowDefRepository flowDefRepository,
                             FlowInstanceRepository flowInstanceRepository,
                             FlowTaskDefRepository flowTaskDefRepository,
                             FlowTypeRepository flowTypeRepository,
                             FlowInstanceTaskRepository flowInstanceTaskRepository) {
        this.flowDefRepository = flowDefRepository;
        this.flowTaskDefRepository = flowTaskDefRepository;
        this.flowInstanceRepository = flowInstanceRepository;
        this.flowTypeRepository = flowTypeRepository;
        this.flowInstanceTaskRepository = flowInstanceTaskRepository;
    }

    /**
     * 启动流程
     *
     * @param flowDefineCode 流程定义编码
     * @param extParam       业务扩展参数
     * @param callBack       业务回调
     */
    @Transactional
    public Integer startInstance(Integer flowDefineCode, String extParam, FlowCallBack callBack) {
        FlowType flowType = flowTypeRepository.flowType(flowDefineCode);
        if (flowType == null) FlowExceptionEnum.FLOW_DEF_NOT_EXIST.throwException();
        FlowDef flowDef = flowDefRepository.functionFlowDef(flowType.getModuleId(), flowType.getId(), FlowDefStatusEnum.ENABLE.getCode());
        if (flowDef == null) FlowExceptionEnum.FLOW_DEF_NOT_EXIST.throwException();
        FlowInstance flowInstance = new FlowInstance();
        flowInstance.setFlowDefId(flowDef.getId());
        flowInstance.setExtParam(extParam);
        flowInstance.setStatus(FlowStatusEnum.INSTANCE_TODO.getCode());
        flowInstance = flowInstanceRepository.save(flowInstance);
        List<FlowTaskDef> flowTaskDefs = flowTaskDefRepository.taskDefs(flowInstance.getFlowDefId());
        if (flowTaskDefs == null) FlowExceptionEnum.FLOW_TASK_DEF_NOT_EXIST.throwException();
        List<FlowInstanceTask> flowInstanceTasks = flowInstanceTasks(flowInstance, flowTaskDefs);
        flowInstanceTasks = flowInstanceTaskRepository.save(flowInstanceTasks);
        flowInstanceTasks = flowInstanceTasks.stream().sorted(Comparator.comparing(FlowInstanceTask::getTaskStep)).collect(Collectors.toList());
        int size = flowInstanceTasks.size();
        flowInstance.setActiveTaskId(flowInstanceTasks.get(0).getId());
        flowInstanceRepository.save(flowInstance);
        for (int i = 0; i < size; i++) {
            FlowInstanceTask instanceTask = flowInstanceTasks.get(i);
            instanceTask.setNextTaskId(i + 1 == size ? 0 : flowInstanceTasks.get(i + 1).getId());
        }
        flowInstanceTaskRepository.save(flowInstanceTasks);
        flowCallBack(flowInstance, null, callBack);
        return flowInstance.getFlowDefId();
    }

    private List<FlowInstanceTask> flowInstanceTasks(FlowInstance flowInstance, List<FlowTaskDef> flowTaskDefs) {
        return flowTaskDefs.stream().map(flowTaskDef -> {
            FlowInstanceTask task = new FlowInstanceTask();
            BeanUtils.copyProperties(flowTaskDef, task);
            task.setId(null);
            task.setFlowDefId(flowInstance.getFlowDefId());
            task.setFlowTaskDefId(flowTaskDef.getId());
            task.setFlowInstanceId(flowInstance.getId());
            task.setNextTaskId(0);
            task.setStatus(FlowStatusEnum.TASK_TODO.getCode());
            task.setOperatorId(flowInstance.getOperatorId());
            task.setCreateTime(flowInstance.getCreateTime());
            task.setModiferId(flowInstance.getModiferId());
            task.setModifyTime(flowInstance.getModifyTime());
            return task;
        }).collect(Collectors.toList());
    }

    /**
     * 通过
     *
     * @param flowInstanceId 流程实例
     * @param flowApprover   审核人
     * @param callBack       业务回调
     */
    public Integer complete(Integer flowInstanceId, FlowApprover flowApprover, FlowCallBack callBack) {
        FlowInstance flowInstance = flowInstanceRepository.flowInstance(flowInstanceId);
        if (flowInstance == null) FlowExceptionEnum.INSTANCE_NOT_EXIST.throwException();
        if (FlowStatusEnum.INSTANCE_COMPLETE.getCode().equals(flowInstance.getStatus()) ||
                FlowStatusEnum.INSTANCE_REJECT.getCode().equals(flowInstance.getStatus()))
            FlowExceptionEnum.INSTANCE_END.throwException();
        FlowInstanceTask flowInstanceTask = flowInstanceTaskRepository.flowInstanceTask(flowInstance.getActiveTaskId());
        if (flowInstanceTask == null) {
            // 更新审批实例状态
            flowInstance.setStatus(FlowStatusEnum.INSTANCE_COMPLETE.getCode());
            flowInstance.setActiveTaskId(0);
            flowInstance.setModiferId(flowApprover.getUserId());
            flowInstance.setModifyTime(new Date());
            flowInstanceRepository.save(flowInstance);
        } else {
            // 验证权限
            flowInstanceTask.verify(flowApprover);
            // 更新审批实例任务节点状态
            flowInstanceTask.setStatus(FlowStatusEnum.TASK_COMPLETE.getCode());
            flowInstanceTask.setApproverId(flowApprover.getUserId());
            flowInstanceTask.setApproverName(flowApprover.getUserName());
            flowInstanceTask.setApproverDept(flowApprover.getDeptId());
            flowInstanceTask.setApproverTime(new Date());
            flowInstanceTaskRepository.save(flowInstanceTask);
            // 更新审批实例状态
            flowInstance.setActiveTaskId(flowInstanceTask.getNextTaskId());
            flowInstance.setStatus(flowInstance.getActiveTaskId() == 0 ? FlowStatusEnum.INSTANCE_COMPLETE.getCode() : FlowStatusEnum.INSTANCE_DOING.getCode());
            flowInstance.setModifyTime(new Date());
            flowInstanceRepository.save(flowInstance);
        }
        // 回调
        flowCallBack(flowInstance, flowApprover, callBack);
        return flowInstanceId;
    }

    /**
     * 拒绝
     *
     * @param flowInstanceId 流程实例
     * @param flowApprover   审核人
     * @param callBack       业务回调
     */
    public Integer reject(Integer flowInstanceId, FlowApprover flowApprover, FlowCallBack callBack) {
        FlowInstance flowInstance = flowInstanceRepository.flowInstance(flowInstanceId);
        if (flowInstance == null) FlowExceptionEnum.INSTANCE_NOT_EXIST.throwException();
        if (FlowStatusEnum.INSTANCE_COMPLETE.getCode().equals(flowInstance.getStatus()) ||
                FlowStatusEnum.INSTANCE_REJECT.getCode().equals(flowInstance.getStatus()))
            FlowExceptionEnum.INSTANCE_END.throwException();
        FlowInstanceTask flowInstanceTask = flowInstanceTaskRepository.flowInstanceTask(flowInstance.getActiveTaskId());
        if (flowInstanceTask == null) {
            // 更新审批实例状态
            flowInstance.setStatus(FlowStatusEnum.INSTANCE_REJECT.getCode());
            flowInstance.setActiveTaskId(0);
            flowInstance.setModiferId(flowApprover.getUserId());
            flowInstance.setModifyTime(new Date());
            flowInstanceRepository.save(flowInstance);
        } else {
            // 验证权限
            flowInstanceTask.verify(flowApprover);
            // 更新审批实例任务节点状态
            flowInstanceTask.setStatus(FlowStatusEnum.TASK_REJECT.getCode());
            flowInstanceTask.setApproverId(flowApprover.getUserId());
            flowInstanceTask.setApproverName(flowApprover.getUserName());
            flowInstanceTask.setApproverDept(flowApprover.getDeptId());
            flowInstanceTask.setApproverTime(new Date());
            flowInstanceTaskRepository.save(flowInstanceTask);
            // 更新审批实例状态
            flowInstance.setActiveTaskId(0);
            flowInstance.setStatus(FlowStatusEnum.INSTANCE_REJECT.getCode());
            flowInstance.setModifyTime(new Date());
            flowInstanceRepository.save(flowInstance);
        }
        // 回调
        flowCallBack(flowInstance, flowApprover, callBack);
        return flowInstanceId;
    }

    private void flowCallBack(FlowInstance flowInstance, FlowApprover flowApprover, FlowCallBack flowCallBack) {
        if (flowCallBack == null) return;
        FlowEvent flowEvent = new FlowEvent();
        flowEvent.setFlowId(flowInstance.getId());
        flowEvent.setExtParam(flowInstance.getExtParam());
        flowEvent.setApprover(flowApprover);
        try {
            if (FlowStatusEnum.INSTANCE_TODO.getCode().equals(flowInstance.getStatus())) {
                flowCallBack.start(flowEvent);
            } else if (FlowStatusEnum.INSTANCE_DOING.getCode().equals(flowInstance.getStatus())) {
                flowCallBack.doing(flowEvent);
            } else if (FlowStatusEnum.INSTANCE_COMPLETE.getCode().equals(flowInstance.getStatus())) {
                flowCallBack.pass(flowEvent);
            } else if (FlowStatusEnum.INSTANCE_REJECT.getCode().equals(flowInstance.getStatus())) {
                flowCallBack.reject(flowEvent);
            }
        } catch (Exception e) {
            LOGGER.error("flowCallBack {}", JsonUtil.objectToJson(flowEvent));
        }
    }

}
